#include "cdlist.h"

typedef struct CDListElmt_tag
{
	void*		data;

	CDListElmt* next;
	CDListElmt* prev;
}CDListElmt;

typedef struct CDList_tag
{
	int size;

	void (*match)(const void* key1, const void* key2);
	void (*destroy)(void* data);

	CDListElmt* head;
}CDList;

CDList* cdlist_create(void (*destroy)(void* data))
{
	CDList* list;
	assert(NULL != (list=(CDList* )malloc(sizeof(CDList))));
	assert(NULL != destroy);

	list->size = 0;
	
	list->head = NULL;

	list->destroy = destroy;

	return list;
}
void cdlist_destroy(CDList* list)
{
	void* data;
	assert(NULL != list);
	assert(NULL != list->destroy);

	while(list->size > 0)
	{
		if (cdlist_rmv(list, list->head, &data)==0)
		{
			list->destroy(data);
		}
	}
	free(list);
	list = NULL;
}

int cdlist_ins_prev(CDList* list, \
					CDListElmt* element,const void* data)
{
	CDListElmt* new_element;
	assert(NULL != (new_element=(CDListElmt* )malloc(sizeof(CDListElmt))));
	assert(NULL != list);
	new_element->data = (void* )data;

	if (list->size == 0)				//empty list
	{
		new_element->next = new_element;
		new_element->prev = new_element;
		list->head = new_element;
	} 
	else
	{
		assert(NULL != element);
		new_element->next = element;
		new_element->prev = element->prev;
		element->prev->next = new_element;
		element->prev = new_element;
		
		if (cdlist_is_head(list,element))
		{
			list->head = list->head->next;
		}
	}

	list->size++;
	return 0;
}
int cdlist_ins_next(CDList* list, \
					CDListElmt* element, const void* data)
{
	CDListElmt* new_element;
	assert((new_element=(CDListElmt* )malloc(sizeof(CDListElmt)))!=NULL);
	assert(NULL != list);
	new_element->data = (void* )data;

	if (list->size == 0)
	{
		new_element->next = new_element;
		new_element->prev = new_element;
		list->head = new_element;
	}
	else
	{
		assert(NULL != element);
		new_element->next = element->next;
		new_element->prev = element;
		element->next->prev = new_element;
		element->next = new_element;
	}
	
	list->size++;
	return 0;
}
int cdlist_rmv(CDList* list, \
			   CDListElmt* element, void** data)
{
	assert(NULL != list && list->size > 0);
	assert(NULL != element);
	*data = element->data;

	if (list->size == 1)						//just one element
	{
		list->head = NULL;
	}
	else
	{
		element->prev->next = element->next;
		element->next->prev = element->prev;
		if (cdlist_is_head(list,element))		//if head is removed
		{
			list->head = list->head->next;
		}
	}

	free(element);
	list->size--;

	return 0;
}

int cdlist_size(const CDList* list)
{
	return list->size;
}
CDListElmt* cdlist_head(const CDList* list)
{
	return list->head;
}
int cdlist_is_head(const CDList* list,\
				   const CDListElmt* element)
{
	return ((list)->head == element ? 1 : 0);
}
int cdlist_is_mbr(const CDList* list, \
				  const CDListElmt* element)
{
	return 0;
}
void* cdlist_data(const CDListElmt* element)
{
	return element->data;
}
CDListElmt* cdlist_next(const CDListElmt* element)
{
	return element->next;
}
CDListElmt* cdlist_prev(const CDListElmt* element)
{
	return element->prev;
}

